package com.meaningful.thinking.schedule_demo;

import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.locks.LockSupport;

public class  ScheduleService {

    ExecutorService executorService = Executors.newFixedThreadPool(6);

    Trigger trigger = new Trigger();

    /**
     * 10位时间戳（如 1620000000）：
     * <p>
     * 表示的是秒级Unix时间戳（自1970-01-01起的秒数）
     * <p>
     * 实际不包含毫秒信息，但常被误称为"毫秒时间戳"
     * <p>
     * 13位时间戳（如 1620000000123）：
     * <p>
     * 真正的毫秒级Unix时间戳
     * <p>
     * 后三位数字（123）代表毫秒部分
     * <p>
     * 其他变体：
     * <p>
     * 16位时间戳（微秒级，如 1620000000123456）
     * <p>
     * 19位时间戳（纳秒级，如 1620000000123456789）
     *
     * @param task  执行任务逻辑
     * @param delay 延迟毫秒数
     */
    void schedule(Runnable task, long delay) {
        Job job = new Job();
        job.setTask(task);
        job.setStartTime(System.currentTimeMillis() + delay);
        job.setDelay(delay);
        trigger.jobQueue.offer(job);
        trigger.wakeup();
    }

    // 等待合适的时间将 任务线程 投递至线程池中
    class Trigger {

        // 这个线程需要拿到应该执行的所有的任务
        // 从哪里拿，容器中拿
        PriorityBlockingQueue<Job> jobQueue = new PriorityBlockingQueue<>();

        Thread thread = new Thread(() -> {
            // 假设容器是空的话，死循环空转十分消耗CPU资源
            while (true) {
                // 没有任务就一直阻塞，一定要考虑虚假唤醒
                while (jobQueue.isEmpty()) {
                    /**
                     * 虚假唤醒是指 线程在没有被显式唤醒（如 notify()、unpark() 或中断）的情况下，
                     * 莫名其妙地从 wait()、park() 或条件变量的等待中返回。这种现象是操作系统或 JVM 的底层实现导致的，无法完全避免。
                     */
                    LockSupport.park();
                }
                synchronized (ScheduleService.class) {
                    // 被唤醒了,看一眼最近的job【这里小心并发问题】
                    Job topJob = jobQueue.peek();
                    if (topJob.getStartTime() <= System.currentTimeMillis()) {
                        topJob = jobQueue.poll();
                        if (Objects.nonNull(topJob) && Objects.nonNull(topJob.getTask())) {
                            executorService.execute(topJob.getTask());
                            Job nextJob = new Job();
                            nextJob.setTask(topJob.getTask());
                            nextJob.setStartTime(System.currentTimeMillis() + topJob.getDelay());
                            nextJob.setDelay(topJob.getDelay());
                            jobQueue.offer(nextJob);
                        }
                    } else {
                        LockSupport.parkUntil(topJob.getStartTime());
                    }
                }

//                if (!jobQueue.isEmpty()) {
//                    Job job = jobQueue.poll();
//                    long timeGap = job.getStartTime() - System.currentTimeMillis();
//                    if (timeGap > 0) {
//                        // 这里会出现并发问题，例如中途添加来一个任务只需要1s后就要执行，而这里等待10s，这样的情况下就会出现后续任务被无视
//                        // 所以这里我们需要停止等待,插队，进行新的一轮获取任务
//                        LockSupport.park();
//                    }
//                    executorService.execute(job.getTask());
//                }
                // 假设存在多个任务,如果后一个任务的开始时间大于前一个任务的开始时间则会导致后一个被阻塞住，从而不会执行
                // 所以需要考虑任务的排序
                // pass
            }
        });

        {
            System.out.println(Thread.currentThread().getName() + "触发器启动......");
            thread.start();
        }

        void wakeup() {
            LockSupport.unpark(thread);
        }
    }

}
